
#include <BScript>
#include "ScriptFormat.h"

ScriptFormat::ScriptFormat()
{

}
ScriptFormat::~ScriptFormat()
{

}

void ScriptFormat::setScript(const BScript* script)
{
	_script = script;
}
const BScript* ScriptFormat::script() const
{
	return _script;
}

void ScriptFormat::update(const BString& text)
{
	int pos = 0;
	BString word;
	while (pos < text.size())
	{
		int begin = nextWord(word, text, pos);
		auto it = _itemMap.insert(begin);
		it->range.set(begin, pos);
		if (word.beginWith('\"'))
		{
			it->color.set(128, 10, 10);
			continue;
		}
		if (word.beginWith('$') || word.beginWith('@'))
		{
			it->color.set(128, 128, 10);
			continue;
		}
		if (word == "nil")
		{
			it->color.set(128, 32, 64);
			continue;
		}
		if (word == "true" || word == "false")
		{
			it->color.set(56, 100, 200);
			continue;
		}
		if (word == "self")
		{
			it->color.set(128, 32, 128);
			continue;
		}
		if (word == "begin" || word == "end")
		{
			it->color.set(64, 86, 128);
			continue;
		}
		if (word == "include")
		{
			it->color.set(123, 100, 86);
			continue;
		}
		if (checkType(word))
		{
			it->color.set(56, 128, 200);
			continue;
		}
		if (checkSyntax(word))
		{
			it->color.set(128, 32, 128);
			continue;
		}
		if (_script && _script->findClass(word))
		{
			it->color.set(36, 100, 66);
			continue;
		}
	}
}

ScriptFormat::ItemMap& ScriptFormat::itemMap()
{
	return _itemMap;
}
const ScriptFormat::ItemMap& ScriptFormat::itemMap() const
{
	return _itemMap;
}

const BFont* ScriptFormat::font(int pos) const
{
	auto it = _itemMap.lessEqual(pos);
	if (it.valid())
	{
		if (pos < it->range.max())
			return it->font;
	}
	return 0;
}
const BColor* ScriptFormat::back(int pos) const
{
	return 0;
}
const BColor* ScriptFormat::color(int pos) const
{
	auto it = _itemMap.lessEqual(pos);
	if (it.valid())
	{
		if (pos < it->range.max())
			return &it->color;
	}
	return 0;
}

int ScriptFormat::indent(int line) const
{
	return -1;
}
int ScriptFormat::lineHeight(int line) const
{
	return -1;
}
const BColor* ScriptFormat::background(int line) const
{
	return 0;
}

int ScriptFormat::nextWord(BString& word, const BString& text, int& pos)
{
	char c = 0;
	while (pos < text.size())
	{
		c = text[pos];
		if (!isspace(c))
			break;
		pos++;
	}
	if (pos == text.size())
	{
		word.reset();
		return text.size();
	}
	int begin = pos;
	if (c == ':' && text[pos + 1] == ':')
	{
		word = "::";
		pos += 2;
		return begin;
	}
	if (c == '\"')
	{
		int begin = pos++;
		pos = text.find('\"', pos);
		word.reset(text, begin, ++pos);
		return begin;
	}
	if (c == '$' || c == '@')
	{
		int begin = pos++;
		pos = text.word(word, pos);
		word.prepend(c);
		return begin;
	}
	if (c == '+' && text[pos + 1] == '+')
	{
		word = "++";
		pos += 2;
		return begin;
	}
	if (c == '-' && text[pos + 1] == '-')
	{
		word = "--";
		pos += 2;
		return begin;
	}
	if (c == '.' || c == ',' || c == ';' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}')
	{
		word = c;
		pos += 1;
		return begin;
	}
	if (c == '<' || c == '>' || c == '!' || c == '+' || c == '-' || c == '*' || c == '/' || c == '|')
	{
		word = c;
		if (text[pos + 1] == '=')
		{
			word << '=';
			pos += 2;
			return begin;
		}
		pos++;
		return begin;
	}
	if (isdigit(text[pos]))
	{
		int end = pos;
		while (++end < text.size())
		{
			if (text[end] == '.' && !isdigit(text[end + 1]))
				break;
			if (!isdigit(text[end]))
				break;
		}
		word.reset(text, begin, end);
		pos = end;
		return begin;
	}
	if (ispunct(text[pos]))
	{
		int end = pos;
		while (end < text.size())
		{
			if (isspace(text[end]))
				break;
			if (!ispunct(text[end]))
				break;
			end++;
		}
		word.reset(text, begin, end);
		pos = end;
		return begin;
	}
	while (pos < text.size())
	{
		if (isspace(text[pos]))
			break;
		if (ispunct(text[pos]))
			break;
		pos++;
	}
	word.reset(text, begin, pos);
	return begin;
}
bool ScriptFormat::checkType(const BString& text)
{
	if (text == "void")
		return true;
	if (text == "bool")
		return true;
	if (text == "char")
		return true;
	if (text == "byte")
		return true;
	if (text == "short")
		return true;
	if (text == "ushort")
		return true;
	if (text == "int")
		return true;
	if (text == "uint")
		return true;
	if (text == "long")
		return true;
	if (text == "ulong")
		return true;
	if (text == "float")
		return true;
	if (text == "double")
		return true;
	if (text == "string")
		return true;
	return false;
}
Syntax ScriptFormat::checkSyntax(const BString& text)
{
	if (text == "class")
		return Syntax_Class;
	if (text == "public")
		return Syntax_Public;
	if (text == "protected")
		return Syntax_Protected;
	if (text == "private")
		return Syntax_Private;
	if (text == "const")
		return Syntax_Const;
	if (text == "viratual")
		return Syntax_Virtual;
	if (text == "inline")
		return Syntax_Inline;
	if (text == "if")
		return Syntax_If;
	if (text == "else")
		return Syntax_Else;
	if (text == "elif")
		return Syntax_Else;
	if (text == "for")
		return Syntax_For;
	if (text == "while")
		return Syntax_While;
	if (text == "switch")
		return Syntax_Switch;
	if (text == "case")
		return Syntax_Case;
	if (text == "default")
		return Syntax_Default;
	if (text == "continue")
		return Syntax_Continue;
	if (text == "break")
		return Syntax_Break;
	if (text == "return")
		return Syntax_Return;
	return Syntax_None;
}
